package org.jeecg.modules.ocr;


import com.visual.open.anpr.core.domain.ExtParam;
import com.visual.open.anpr.core.domain.ImageMat;
import com.visual.open.anpr.core.domain.PlateImage;
import com.visual.open.anpr.core.domain.PlateInfo;
import com.visual.open.anpr.core.extract.PlateExtractor;
import com.visual.open.anpr.model.*;
import com.visual.open.anpr.utils.Base64Util;
import org.apache.http.client.config.RequestConfig;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.ocr.einvoice.Invoice;
import org.jeecg.modules.ocr.einvoice.OfdInvoiceExtractor;
import org.jeecg.modules.ocr.einvoice.PdfInvoiceExtractor;
import org.jeecg.modules.ocr.utils.OCRUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * <p>
 *  OCR识别
 * </p>
 *
 * @author 29shusheng
 * @date 2024-7-25 17:55:46
 */
@RestController
@RequestMapping("/ocr")
public class OcrController {

    @Value(value = "${jeecg.path.upload}")
    private String uploadpath;
    @Value(value="${jeecg.uploadType}")
    private String uploadType;
    @Value("${backupPath}")
    private String backupPath;
    @Resource
    private PlateExtractor plateExtractor;


    private static ThreadLocal<Map<String, DateFormat>> threadLocal = new ThreadLocal<>();
    private static final String FILE_NAME_FORMAT_STRING = "yyyy/MM-dd/HH-mm-ssSSSS";
    public static final RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000)
            .setConnectionRequestTimeout(5000).build();

    /**
     * 通用识别图片
     * @return 识别结果
     * @throws IOException
     */
    /*@PostMapping("/generalOcr1")
    public String ocr(@RequestParam("file") MultipartFile fileUpload) throws Exception {
        return OCRUtil.ocrResult(fileUpload);
    }
*/
    /**
     * 识别图片是不是身份证
     * @param fileUpload 图片
     * @return
     * @throws IOException
     */
    /*@PostMapping("/idcard")
    public Boolean ocrIdcard(@RequestParam("file") MultipartFile fileUpload) throws IOException {
        String string = OCRUtil.ocrResult(fileUpload);
        return OCRUtil.ocrIdcard(string);
    }

    *//**
     * 通用识别
     * @return
     * @throws IOException
     *//*
    @PostMapping("/bdc")
    public Boolean ocrBdc(@RequestParam("file") MultipartFile fileUpload) throws IOException {
        String string = OCRUtil.ocrResult(fileUpload);
        return OCRUtil.ocrBdc(string);
    }*/

    @PostMapping(value = "/generalOcr")
    public Result<?> generalOcr(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String savePath = "";
        String bizPath = request.getParameter("biz");
        //1通用识别2身份证判断3不动产权证判断4发票识别5车牌识别
        String ocrType = request.getParameter("ocrType");

        //LOWCOD-2580 sys/common/upload接口存在任意文件上传漏洞
        if (oConvertUtils.isNotEmpty(bizPath)) {
            if(bizPath.contains(SymbolConstant.SPOT_SINGLE_SLASH) || bizPath.contains(SymbolConstant.SPOT_DOUBLE_BACKSLASH)){
                throw new JeecgBootException("上传目录bizPath，格式非法！");
            }
        }

        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        // 获取上传文件对象
        MultipartFile file = multipartRequest.getFile("file");
        if(oConvertUtils.isEmpty(bizPath)){
            if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
                //未指定目录，则用阿里云默认目录 upload
                bizPath = "upload";
            }else{
                bizPath = "";
            }
        }
        if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
            //update-begin-author:liusq date:20221102 for: 过滤上传文件类型
            SsrfFileTypeFilter.checkUploadFileType(file);
            //update-end-author:liusq date:20221102 for: 过滤上传文件类型
            //update-begin-author:lvdandan date:20200928 for:修改JEditor编辑器本地上传
            savePath = this.uploadLocal(file,bizPath);
        }else{
            //update-begin-author:taoyan date:20200814 for:文件上传改造
            savePath = CommonUtils.upload(file, bizPath, uploadType);
            //update-end-author:taoyan date:20200814 for:文件上传改造
        }
        if(oConvertUtils.isNotEmpty(savePath)){
            if ("1".equals(ocrType)){
                String ocrResult = OCRUtil.ocrResult(file);
                return Result.OK(savePath, ocrResult);
            }
            if ("2".equals(ocrType)){
                String ocrResult = OCRUtil.ocrResult(file);
                ocrResult = String.valueOf(OCRUtil.ocrIdcard(ocrResult));
                return Result.OK(savePath, ocrResult);
            }
            if ("3".equals(ocrType)){
                String ocrResult = OCRUtil.ocrResult(file);
                ocrResult = String.valueOf(OCRUtil.ocrBdc(ocrResult));
                return Result.OK(savePath, ocrResult);
            }
            if ("4".equals(ocrType)){
                Invoice invoice = invoiceExtrat(file, bizPath);
                return Result.OK(savePath, invoice);
            }
            if ("5".equals(ocrType)){
                List<RecognitionRep> recognitionReps = carOcr(file);
                return Result.OK(savePath, recognitionReps);
            }
        }else {
            return Result.error("上传失败！");
        }
        return Result.error("识别失败！");
    }

    /**
     * 本地文件上传
     * @param mf 文件
     * @param bizPath  自定义路径
     * @return
     */
    private String uploadLocal(MultipartFile mf,String bizPath){
        try {
            String ctxPath = uploadpath;
            String fileName = null;
            File file = new File(ctxPath + File.separator + bizPath + File.separator );
            if (!file.exists()) {
                // 创建文件根目录
                file.mkdirs();
            }
            // 获取文件名
            String orgName = mf.getOriginalFilename();
            orgName = CommonUtils.getFileName(orgName);
            if(orgName.indexOf(SymbolConstant.SPOT)!=-1){
                fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
            }else{
                fileName = orgName+ "_" + System.currentTimeMillis();
            }
            String savePath = file.getPath() + File.separator + fileName;
            File savefile = new File(savePath);
            FileCopyUtils.copy(mf.getBytes(), savefile);
            String dbpath = null;
            if(oConvertUtils.isNotEmpty(bizPath)){
                dbpath = bizPath + File.separator + fileName;
            }else{
                dbpath = fileName;
            }
            if (dbpath.contains(SymbolConstant.DOUBLE_BACKSLASH)) {
                dbpath = dbpath.replace(SymbolConstant.DOUBLE_BACKSLASH, SymbolConstant.SINGLE_SLASH);
            }
            return dbpath;
        } catch (IOException e) {
            //log.error(e.getMessage(), e);
        }
        return "";
    }


    /**
     * 发票识别
     * @param fileUpload
     * @param bizPath
     * @return
     * @throws IOException
     */
    public Invoice invoiceExtrat(MultipartFile fileUpload, String bizPath) throws IOException {
        boolean ofd = false;
        String ctxPath = uploadpath;
        String fileName = null;
        File file = new File(ctxPath + File.separator + bizPath + File.separator );
        if (!file.exists()) {
            // 创建文件根目录
            file.mkdirs();
        }
        // 获取文件名
        String orgName = fileUpload.getOriginalFilename();
        orgName = CommonUtils.getFileName(orgName);
        if(orgName.indexOf(SymbolConstant.SPOT)!=-1){
            fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
        }else{
            fileName = orgName+ "_" + System.currentTimeMillis();
        }
        String savePath = file.getPath() + File.separator + fileName;
        File savefile = new File(savePath);
        FileCopyUtils.copy(fileUpload.getBytes(), savefile);

        if (fileUpload.getOriginalFilename().toLowerCase().endsWith(".ofd")) {
            ofd = true;
        }

        Invoice result = null;
        try {
            if (null != savefile) {
                if (ofd) {
                    result = OfdInvoiceExtractor.extract(savefile);
                } else {
                    result = PdfInvoiceExtractor.extract(savefile);
                }
                if (null != result.getAmount()) {
                    savefile.delete();
                }
            } else {
                result = new Invoice();
                result.setTitle("error");
            }
        } catch (Exception e) {
            e.printStackTrace();
            result = new Invoice();
            result.setTitle("error");
        }
        return result;
    }

    /**
     * 车辆识别
     * @param fileUpload
     * @return
     */
    public List<RecognitionRep> carOcr(MultipartFile fileUpload) throws IOException {
        File file = new File("D:\\scholar-ai\\file");
        if (!file.exists()) {
            // 创建文件根目录
            file.mkdirs();
        }
        fileUpload.transferTo(file);
        file.deleteOnExit();
        String imageBase64 = Base64Util.encode(file.getAbsolutePath());
        RecognitionReq plateInfoReq = new RecognitionReq();
        plateInfoReq.setImage(imageBase64);
        plateInfoReq.setLimit(5);
        //模型检测车牌信息
        ImageMat imageMat = null;
        PlateImage plateImage = null;
        try {
            int topK = (null == plateInfoReq.getLimit() || plateInfoReq.getLimit()  <= 0) ? 5 : plateInfoReq.getLimit();
            ExtParam extParam = ExtParam.build().setTopK(topK);
            imageMat = ImageMat.fromBase64(plateInfoReq.getImage());
            plateImage = plateExtractor.extract(imageMat, extParam, new HashMap<>());
        }finally {
            if(null != imageMat){
                imageMat.release();
            }
        }
        if(null == plateImage){
            throw new RuntimeException("PlateExtractor extract error");
        }
        //转换模型结果，并输出
        List<RecognitionRep> plates = new ArrayList<>();
        if(null != plateImage.PlateInfos() && !plateImage.PlateInfos().isEmpty()){
            for(PlateInfo plateInfo : plateImage.PlateInfos()){
                //检测信息
                RecognitionRep plate = new RecognitionRep();
                plate.setScore((float)Math.floor(plateInfo.score * 1000000)/10000);
                PlateLocation location = new PlateLocation();
                location.setLeftTop(new LocationPoint(plateInfo.box.leftTop.x, plateInfo.box.leftTop.y));
                location.setRightTop(new LocationPoint(plateInfo.box.rightTop.x, plateInfo.box.rightTop.y));
                location.setRightBottom(new LocationPoint(plateInfo.box.rightBottom.x, plateInfo.box.rightBottom.y));
                location.setLeftBottom(new LocationPoint(plateInfo.box.leftBottom.x, plateInfo.box.leftBottom.y));
                plate.setLocation(location);

                //识别信息
                RecognitionInfo recognition = new RecognitionInfo();
                recognition.setLayout(plateInfo.single ? PlateLayout.SINGLE : PlateLayout.DOUBLE);
                recognition.setPlateNo(plateInfo.parseInfo.plateNo);
                recognition.setPlateColor(PlateColor.valueOfName(plateInfo.parseInfo.plateColor));
                plate.setRecognition(recognition);
                //添加车牌
                plates.add(plate);
            }
        }
        return plates;
    }

}
